package com.dexgdx.game;

import java.util.Collection;
import java.util.Collections;
import java.util.ConcurrentModificationException;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.InputMultiplexer;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.ParticleEffectPool.PooledEffect;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
import com.badlogic.gdx.input.GestureDetector;
import com.badlogic.gdx.input.GestureDetector.GestureListener;
import com.badlogic.gdx.math.Intersector;
import com.badlogic.gdx.math.Polygon;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.utils.viewport.*;
import com.dexgdx.game.action.IAction;
import com.dexgdx.game.action.pack.ActionPack;
import com.dexgdx.game.constants.EMoveDirection;
import com.dexgdx.game.constants.ERingColor;
import com.dexgdx.game.manager.ActorManager;
import com.dexgdx.game.manager.AnimationInfoKey;
import com.dexgdx.game.manager.AnimationManager;
import com.dexgdx.game.structure.actor.AbstractActor;
import com.dexgdx.game.structure.actor.EnemyActor;
import com.dexgdx.game.structure.actor.PlayerActor;
import com.dexgdx.game.structure.base.BaseObject;
import com.dexgdx.game.structure.base.BasePoint;
import com.dexgdx.game.structure.base.BaseRegion;
import com.dexgdx.game.structure.base.MobileObject;
import com.dexgdx.game.structure.projectile.DexMissileObject;
import com.dexgdx.game.structure.projectile.DexTrailObject;
import com.dexgdx.game.util.trajectory.node.TrajectoryNode;

public class DexGdxGame extends ApplicationAdapter implements GestureListener, InputProcessor {

	private OrthographicCamera camera;
	private Viewport viewPort;

    private ShapeRenderer renderer;
    private SpriteBatch spriteBatch;

    private static int WORLD_HEIGHT = 989;
    private static int WORLD_WIDTH = 1790;
    
    TextureRegion backMap1;
    
    BasePoint camMoveTrajection;
    float aspectRatio;
    
	@Override
	public void create () { 
		
		renderer = new ShapeRenderer();
		spriteBatch = new SpriteBatch();
		
		aspectRatio = (float)Gdx.graphics.getWidth() / (float)Gdx.graphics.getHeight();

		backMap1 = new TextureRegion(new Texture(Gdx.files.internal("data/maps/map1.jpg")));
		backMap1.flip(false, true);
		camMoveTrajection = new BasePoint();
//		WORLD_HEIGHT = backMap1.getRegionHeight();
//		WORLD_WIDTH = backMap1.getRegionWidth();
		
		camera = new OrthographicCamera();
		camera.setToOrtho(true);
		camera.position.set(450, 450, 0);
		viewPort = new FillViewport(900, 900, camera);
		viewPort.apply();
        InputMultiplexer im = new InputMultiplexer();
        GestureDetector gd = new GestureDetector(this);
        im.addProcessor(gd);
        im.addProcessor(this);
        Gdx.input.setInputProcessor(im);
        
//		Gdx.graphics.setContinuousRendering(false);
		Gdx.graphics.requestRendering();
		renderer.setProjectionMatrix(camera.combined);
		spriteBatch.setProjectionMatrix(camera.combined);
		
		Game.initialize();
		Game.run();

		Gdx.gl.glLineWidth(1);
		Gdx.gl.glClearColor(0, 0, 0, 1);
		
	}
	
	@Override
	public void resize(int width, int height) {
		super.resize(width, height);
		viewPort.update(900, 900);
		camera.position.set(450, 450, 0);
	}
	
	@Override
	public void render () {
		Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
		camera.position.x += camMoveTrajection.x;
		camera.position.y += camMoveTrajection.y;
		camera.update();
		renderer.setProjectionMatrix(camera.combined);
		spriteBatch.setProjectionMatrix(camera.combined);

		spriteBatch.begin();
		spriteBatch.draw(backMap1, 0, 0, backMap1.getRegionWidth(), backMap1.getRegionHeight());
		spriteBatch.end();
//	      
        Game.stateTime += Gdx.graphics.getDeltaTime();  
        
        Collections.sort(Game.gameData.getBaseObjectList());

//		renderer.begin(ShapeType.Line);
//        renderer.polygon(vertices);
//        renderer.end();
//        if(multiTouching){
//			drawRing(ERingColor.PATH_NODE, 0, 0, 120, 160);
//        }
        int a = 0;
		while (a < Game.touchedList.size()) {
			drawRing(ERingColor.PATH_NODE, Game.touchedList.get(a)); a++;
		}
        if(Game.isSwiping && Game.gameData.isAnyPlayerSelected()){
    		renderer.begin(ShapeType.Line);
    		renderer.setColor(Color.valueOf("FFFFFF"));
    		renderer.line(Game.swipeStartX, Game.swipeStartY, Game.swipeEndX, Game.swipeEndY);
    		renderer.end();

    		renderer.begin(ShapeType.Filled);
    		renderer.setColor(Color.valueOf(ERingColor.SELECTED_ENEMY.getHexCode()));
    		renderer.circle((Game.swipeStartX + Game.swipeEndX)/2, (Game.swipeStartY + Game.swipeEndY)/2, 5);
    		renderer.end();
        }
		drawRing(ERingColor.PATH_NODE,camera.position.x, camera.position.y, 10, 10);
		drawRing(ERingColor.SELECTED_ENEMY,0 - 10, 0 - 10, 20, 20);
        
		int animIndex = 0;
		int index = 0;
		
		while (index < Game.gameData.getBaseObjectList().size()) {
			BaseObject baseObject = Game.gameData.getBaseObjectList().get(index);
			if(Game.gameData.getNonActorObjectList().contains(baseObject) &&
					baseObject instanceof DexTrailObject){
				DexTrailObject dexTrailObject = (DexTrailObject) baseObject;
				spriteBatch.begin();
				for (int i = 0; i < dexTrailObject.getPooledEffects().size(); i++) {
					dexTrailObject.getPooledEffects().get(i).getPooledEffectMapRel().draw(spriteBatch,Gdx.graphics.getDeltaTime());
				}
				spriteBatch.end();
			}
			else 
				if(Game.gameData.getMovingObjects().contains(baseObject)){
				MobileObject mobileObject = (MobileObject) baseObject;
				if(mobileObject != null && 
						mobileObject.getMoveTarget() != null &&
						mobileObject instanceof PlayerActor && 
						!(mobileObject.getMoveTarget() instanceof EnemyActor)){

					drawRing(ERingColor.BEING_TARGET, mobileObject.getMoveTarget().x, mobileObject.getMoveTarget().y, 
							mobileObject.getMoveTarget().height, mobileObject.getMoveTarget().width);
				}
				if(mobileObject instanceof AbstractActor){
					AbstractActor actor = (AbstractActor) mobileObject;
					drawRing(actor.getRingColor(), actor.x, actor.y, actor.height, actor.width);
					actor.loadImageTextureRegionAndFrame(animIndex);
					spriteBatch.begin();
			        spriteBatch.draw(actor.getImageTextureRegion(), actor.getImageBaseRegion().getX(), actor.getImageBaseRegion().getY(),
			        		actor.getImageBaseRegion().width, actor.getImageBaseRegion().height);
			        spriteBatch.end();
			        animIndex ++;
			        
				} else if(mobileObject instanceof DexMissileObject){
					DexMissileObject dexMissileObject = (DexMissileObject) mobileObject;
					spriteBatch.begin();
					dexMissileObject.getPooledEffect().draw(spriteBatch,Gdx.graphics.getDeltaTime());
					spriteBatch.end();
				}
				
			}
			else if(Game.gameData.getEnemyList().contains(baseObject)){
				AbstractActor actor = (EnemyActor) baseObject;
				
				drawRing(actor.getRingColor(), actor.x, actor.y, actor.height, actor.width);
				
				if(!actor.isMoving()){
					actor.loadImageTextureRegionAndStopFrame();
					spriteBatch.begin();
			        spriteBatch.draw(actor.getImageTextureRegion(), actor.getImageBaseRegion().getX(), actor.getImageBaseRegion().getY(),
			        		actor.getImageBaseRegion().width, actor.getImageBaseRegion().height);
			        spriteBatch.end();
				}
			}
			else if(Game.gameData.getPlayerList().contains(baseObject)){
				AbstractActor actor = (PlayerActor) baseObject;

				drawRing(actor.getRingColor(), actor.x, actor.y, actor.height, actor.width);

				if(!actor.isMoving()){
					actor.loadImageTextureRegionAndStopFrame();
					spriteBatch.begin();
			        spriteBatch.draw(actor.getImageTextureRegion(), actor.getImageBaseRegion().getX(), actor.getImageBaseRegion().getY(),
			        		actor.getImageBaseRegion().width, actor.getImageBaseRegion().height);
			        spriteBatch.end();
				}
			}
			index++;
		}

		if(Game.selectionRectangle.isVisible()){
			renderer.begin(ShapeType.Line);
			renderer.setColor(Color.valueOf(ERingColor.SELECTED_PLAYER.getHexCode()));
			renderer.box(Game.selectionRectangle.x, Game.selectionRectangle.y, 0, Game.selectionRectangle.width, Game.selectionRectangle.height, 0);
			renderer.end();
		}
	}
	
	private void drawRing(ERingColor eRingColor, float x, float y, float height, float width){
		renderer.begin(ShapeType.Line);
		renderer.setColor(Color.valueOf(eRingColor.getHexCode()));
//		renderer.box(x, y, 1f, width, height, 1f);
		renderer.ellipse(x, y, width, height);
		renderer.end();
	}
	
	private void drawRing(ERingColor eRingColor, BaseRegion drawRegion){
		drawRing(eRingColor, drawRegion.x, drawRegion.y, drawRegion.height, drawRegion.width);
	}


	public boolean touchDown(int x, int y, int pointer, int button) {
		
		if(Gdx.input.isTouched(1) && pointer == 0){
			getRealPoints(x, y);
			Game.initialMultiTouchPosition.x = newPoints.x;
			Game.initialMultiTouchPosition.y = newPoints.y;
			Game.initialCameraPosition.x = camera.position.x;
			Game.initialCameraPosition.y = camera.position.y;
		}
		
//		multiTouching = false;
//		if(pointer > 0){
//			multiTouching = true;
//			Game.initialMultiTouchPosition.x = x;
//			Game.initialMultiTouchPosition.y = y;
//			Game.initialCameraPosition.x = camera.position.x;
//			Game.initialCameraPosition.y = camera.position.y;
//		}
//		Game.touchedList.add(new BaseRegion(x, y, 100, 100));
		return true;
	}

	public boolean touchUp(int x, int y, int pointer, int button) {

		if(Gdx.input.isTouched(1)){
			camMoveTrajection.y = 0;
			camMoveTrajection.x = 0;
			return false;
		}
		
		getRealPoints(x, y);
		x = (int)newPoints.x;
		y = (int)newPoints.y;

		if(Game.isSwiping){
			Game.screenSwiped();
			return false;
		}
		if(Game.selectionRectangle.isVisible()){
			Game.selectionRectangle.setInvisible();
		}
		else {
			boolean actorTouched = false;
			for (PlayerActor playerActor : Game.gameData.getPlayerList()) {
				if(playerActor.contains(x, y)){
					Game.playerTouched(playerActor);
					actorTouched = true;
					break;
				}
			}
			if(!actorTouched){
				for (EnemyActor enemyActor : Game.gameData.getEnemyList()) {
					if(enemyActor.contains(x, y)){
						Game.enemyTouched(enemyActor);
						actorTouched = true;
						break;
					}
				}
			}
			if(!actorTouched){
				Game.emptyScreenTouched(x, y);
			}
		}
		return true;
	}

	public boolean touchDragged(int x, int y, int pointer) {

		getRealPoints(x, y);
		x = (int)newPoints.x;
		y = (int)newPoints.y;

		if(Gdx.input.isTouched(1)){
			if(pointer == 0){
				camMoveTrajection.x = 0;
				camMoveTrajection.y = 0;
				camera.position.y = Game.initialCameraPosition.y - y + Game.initialMultiTouchPosition.y;
				camera.position.x = Game.initialCameraPosition.x - x + Game.initialMultiTouchPosition.x;
				camera.update();
				renderer.setProjectionMatrix(camera.combined);
				spriteBatch.setProjectionMatrix(camera.combined);
			}
		}
		else if(Game.selectionRectangle.isVisible()){
			
			Game.selectionRectangle.x = 
					(x < Game.selectionRectangle.getStartPointX()) ? x : Game.selectionRectangle.getStartPointX();
			Game.selectionRectangle.y = 
					(y < Game.selectionRectangle.getStartPointY()) ? y : Game.selectionRectangle.getStartPointY();
			
			Game.selectionRectangle.width = Math.abs(Game.selectionRectangle.getStartPointX() - x);
			Game.selectionRectangle.height = Math.abs(Game.selectionRectangle.getStartPointY() - y);
			
			for(PlayerActor playerActor : Game.gameData.getPlayerList()){
				if(Intersector.overlaps(Game.selectionRectangle, playerActor)){
					Game.gameData.selectPlayer(playerActor, true);
				}
				else {
					Game.gameData.deSelectPlayer(playerActor);
				}
			}
		}
		
		return true;
	}

	@Override
	public boolean touchDown(float x, float y, int pointer, int button) {
		return false;
	}

	@Override
	public boolean tap(float x, float y, int count, int button) {
		return false;
	}

	@Override
	public boolean longPress(float x, float y) {

		if(Gdx.input.isTouched(1)){
			return false;
		}
		
		getRealPoints(x, y);
		x = (int)newPoints.x;
		y = (int)newPoints.y;
		
		if(x > 30 && y > 30){
			Game.selectionRectangle.x = x - 50;
			Game.selectionRectangle.y = y - 50;
			Game.selectionRectangle.width = 2*(x - Game.selectionRectangle.x);
			Game.selectionRectangle.height = 2*(y - Game.selectionRectangle.y);
			Game.selectionRectangle.setVisible(x - 50, y - 50);
		}
		
		return false;
	}

	@Override
	public boolean fling(float velocityX, float velocityY, int button) {
		// TODO Auto-generated method stub
		return false;
	}

	Vector2 newPoints = new Vector2();
	@Override
	public boolean pan(float x, float y, float deltaX, float deltaY) {
		
		if(Gdx.input.isTouched(1)){
			return false;
		}
		
		getRealPoints(x, y);
		x = (int)newPoints.x;
		y = (int)newPoints.y;
		
		if(!Game.selectionRectangle.isVisible()){
			if(!Game.isSwiping){
				Game.swipeStartX = x;
				Game.swipeStartY = y;
			}
			Game.swipeEndX = x;
			Game.swipeEndY = y;
			Game.isSwiping = true;
		}
		return false;
	}

	@Override
	public boolean panStop(float x, float y, int pointer, int button) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean zoom(float initialDistance, float distance) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean pinch(Vector2 initialPointer1, Vector2 initialPointer2,
			Vector2 pointer1, Vector2 pointer2) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean keyDown(int keycode) {
		if(keycode == Input.Keys.UP && camera.position.y > 9){
			camMoveTrajection.y = -10;
		}
		else if(keycode == Input.Keys.DOWN){
			camMoveTrajection.y = 10;
		}
		if(keycode == Input.Keys.LEFT && camera.position.x > 9){
			camMoveTrajection.x = -10;
		}
		else if(keycode == Input.Keys.RIGHT){
			camMoveTrajection.x = 10;
		}
		camera.update();
		return false;
	}

	@Override
	public boolean keyUp(int keycode) {
		camMoveTrajection.y = 0;
		camMoveTrajection.x = 0;
		return false;
	}

	@Override
	public boolean keyTyped(char character) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean mouseMoved(int screenX, int screenY) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean scrolled(int amount) {
		// TODO Auto-generated method stub
		return false;
	}
	
	private void getRealPoints(float x, float y){
		newPoints.x = x;
		newPoints.y = y;
		newPoints = viewPort.unproject(newPoints);
	}
}
